/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	SwapChain.cpp
*
*	Comments	-	See SwapChain.h
*
**************************************************************************************/

#include "../Include/SwapChain.h"
//#include "../Include/WindowApplicationWin32.h"
#include "../Include/DX11Mapping.h"
#include "../Include/Renderer.h"
#include "../Include/Texture2D.h"
#include "../Include/GraphicsResourceManager.h"
#include "../Include/AsyncLoadResource.h"
#include "../Include/GlobalGraphicsStrings.h"

namespace Pulse
{
	SwapChain::SwapChain( void )
		: m_pSwapChain( PSX_NULL ), m_pSwapChainBuffer( PSX_NULL ), m_pDepthBuffer( PSX_NULL )
	{

	}

	SwapChain::~SwapChain( void )
	{

	}

	BOOL SwapChain::CreateResource( BaseResDesc *pDesc )
	{
		if ( pDesc->GetType() != GetType() )
			return FALSE;

		m_desc = *((SwapChainDesc*)pDesc);

		if ( PerformInitialize( m_desc.IsAsyncLoad() ) == EErrorCode::OKAY )
			SetFlagCreated();

		return IsCreated();	
	}

	BOOL SwapChain::DestroyResource( void )
	{
		UnloadResource();
		SetFlagDestroyed();	
		return TRUE;	
	}

	BOOL SwapChain::LoadResource( void )
	{
		if ( Initialize() == EErrorCode::OKAY )
		{
			SetFlagLoaded();	
			return TRUE;
		}

		return FALSE;
	}

	BOOL SwapChain::UnloadResource( void )
	{
		Cleanup();
		SetFlagUnloaded();
		return FALSE;	
	}

	Texture2D * SwapChain::GetBackBuffer( void )
	{
		m_pSwapChainBuffer->AddRef();
		return m_pSwapChainBuffer;
	}

	Texture2D * SwapChain::GetBackBufferUnsafe( void )
	{
		return m_pSwapChainBuffer;
	}

	void SwapChain::SetDepthBuffer( Texture2D *pDepthBuffer )
	{
		if ( pDepthBuffer )
			pDepthBuffer->AddRef();

		PSX_SafeRelease( m_pDepthBuffer );

		m_pDepthBuffer = pDepthBuffer;
	}

	Texture2D * SwapChain::GetDepthBuffer( void )
	{
		if ( m_pDepthBuffer )
		{
			m_pDepthBuffer->AddRef();
		}

		return m_pDepthBuffer;
	}

	Texture2D * SwapChain::GetDepthBufferUnsafe( void )
	{
		return m_pDepthBuffer;
	}

	EErrorCode::Type SwapChain::Resize( SIZE_T width, SIZE_T height )
	{
		ReleaseTextureBuffers();

		m_desc.width = width;
		m_desc.height = height;

		// Use this or the other one below?
		if ( FALSE )
		{
			m_pSwapChain->ResizeBuffers( 1, (UINT)width, (UINT)height, 
				DX11Mapping::ToDXBufferFormat( m_desc.format ), 0);
		}
		else
		{
			DXGI_MODE_DESC desc;
			DXGI_RATIONAL refreshRate = { 60, 1 };

			desc.Width = (UINT)width;
			desc.Height = (UINT)height;
			desc.RefreshRate = refreshRate;
			desc.Format = DXGI_FORMAT_UNKNOWN; // Use existing format
			desc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED ;
			desc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

			HRESULT hRes = m_pSwapChain->ResizeTarget( &desc );

			if ( FAILED( hRes ) )
			{
				PSX_PushError( "Failed to resize swapchain" );
				return EErrorCode::GRAPHICS;
			}
		}

		return ExtractTextureBuffers();
	}

	void SwapChain::Present( void )
	{
		m_pSwapChain->Present( 0, 0 );
	}

	EErrorCode::Type SwapChain::ExtractTextureBuffers( void )
	{
		// Release texture buffers before anything else
		ReleaseTextureBuffers();

		HRESULT hr;
		ID3D11Texture2D *pSwapChainbuffer;
		
		hr = m_pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (void**)&pSwapChainbuffer );

		if ( FAILED( hr ) )
		{
			PSX_PushError( "Failed to get swap chain buffer." );
			return EErrorCode::GRAPHICS;
		}

		// Encapsulate swap chain buffer to texture 2d
		GraphicsResourceManager *pMan = GraphicsResourceManager::GetInstance();
		Texture2DDesc desc;
		desc.SetAsSwapChainBuffer( pSwapChainbuffer );
		m_pSwapChainBuffer = pMan->CreateTexture2D( GFX_SWAPCHAIN_BACK_BUFFER, &desc );
		pSwapChainbuffer->Release();

		if ( m_pSwapChain == PSX_NULL )
		{
			PSX_PushError( "Failed to get swap chain buffer Texture2D." );
			return EErrorCode::GRAPHICS;
		}

		// Render path will be responsible for creating the depth buffer.
		m_pDepthBuffer = PSX_NULL;

		return EErrorCode::OKAY;
	}

	void SwapChain::ReleaseTextureBuffers( void )
	{
		PSX_SafeRelease( m_pSwapChainBuffer );
		//PSX_SafeRelease( m_pSwapChain );
	}

	EErrorCode::Type SwapChain::Initialize( void )
	{
		// Initialize renderer
		GraphicsInfrastructure *pGI = GraphicsInfrastructure::GetInstance();
		DXGIFactoryPtr pFactory = pGI->GetFactory();

		DXGI_SWAP_CHAIN_DESC swapChainDesc;
		swapChainDesc.BufferDesc.Width = (UINT)m_desc.width;
		swapChainDesc.BufferDesc.Height = (UINT)m_desc.height;	
		swapChainDesc.BufferDesc.RefreshRate.Numerator = (UINT)m_desc.refreshRate.numerator;	
		swapChainDesc.BufferDesc.RefreshRate.Denominator = (UINT)m_desc.refreshRate.denominator;	
		swapChainDesc.BufferDesc.Format = DX11Mapping::ToDXBufferFormat( m_desc.format );
		swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
		swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

		swapChainDesc.SampleDesc.Count = m_desc.sampleDesc.count;
		swapChainDesc.SampleDesc.Quality = m_desc.sampleDesc.quality;

		swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		swapChainDesc.BufferCount = 1;
		//swapChainDesc.OutputWindow = gpApp->GetHWND();
		swapChainDesc.OutputWindow = (HWND)m_desc.hWnd;
		swapChainDesc.Windowed = m_desc.bWindowed;
		swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
		swapChainDesc.Flags = 0;

		HRESULT hr;
		hr = pFactory->CreateSwapChain( (ID3D11Device*)GraphicsDevice::GetInstance()->GetNakedDevice(), &swapChainDesc, &m_pSwapChain );

		if ( FAILED(hr) )
		{
			PSX_PushError( "Failed to create graphics swap chain." );
			return EErrorCode::GRAPHICS;
		}

		hr = m_pSwapChain->ResizeBuffers( 1, (UINT)m_desc.width, (UINT)m_desc.height, DX11Mapping::ToDXBufferFormat( m_desc.format ), 0 );

		if ( FAILED(hr) )
		{
			PSX_PushError( "Failed to resize graphics swap chain." );
			return EErrorCode::GRAPHICS;
		}

		EErrorCode::Type retCode = ExtractTextureBuffers();

		if ( retCode != EErrorCode::OKAY )
			return retCode;

		// Redundant
		return EErrorCode::NONE;
	}

	void SwapChain::Cleanup( void )
	{
		ReleaseTextureBuffers();
		PSX_SafeRelease( m_pSwapChain );
	}
}